<!DOCTYPE html>
<html>
  <head>    
    <meta charset="utf-8" />    
    <title>Two-Factor One-Time Virtual Book Cypher Demo
    </title>    
    <script type="text/javascript">
      
      /*
      
      Copyright (c) 2013, Fred Scott Thompson
      
    All rights reserved.
    Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    3. Neither the name of the Three Wise Apes nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
            
      */
      
      
      function show( textin, where) {  // this was created to replace the textareas with something lighter but was never used
        var out = "";
        if (textin.length > 600) {
            out =  textin.substr(0,300) + " ... <br /> &nbsp; &nbsp; &nbsp; ... " +   textin.substr(textin.length - 301, 300);
        }   else {
            out = textin;
        }
        document.getElementById(where).innerHTML = out;
      }  
      
      var mode = "encrypt";
      var data = "text";
      var i = 0;
      var j = 0;
      var k = 0;
      var filename = "";
      var filetype = "";
      var fromfile = false;
      var readtype = "txt";
      var view = "functional";
        
      function loadFromFile() {
          document.getElementById("dfilelabel").style.display = "block";
          document.getElementById("dfile").style.display = "blinlineock";
          document.getElementById("pasteinstead").style.display = "";
          document.getElementById("loadfromfile").style.display = "none";
          document.getElementById("dcyphertextIn").value = "File selection will automatically fill this in.";
      }
      
      function neverMind() {
          document.getElementById("dfilelabel").style.display = "none";
          document.getElementById("dfile").style.display = "none";
          document.getElementById("pasteinstead").style.display = "none";
          document.getElementById("loadfromfile").style.display = "inline";
          document.getElementById("dcyphertextIn").value = "Paste the Cyphertext Message here";
      }
      
      function changeType() {
        if (data == "text") {
          document.getElementById("emsgfilelabel").style.display = "block";
          document.getElementById("emsgfile").style.display = "block";
          document.getElementById("erawmsgin").value = "File selection will automatically fill this in.";
          loadFromFile()
          data = "file";
        } else {
          document.getElementById("emsgfilelabel").style.display = "none";
          document.getElementById("emsgfile").style.display = "none"; 
          document.getElementById("erawmsgin").value = "Paste your Plaintext Message here";
          neverMind();
          data = "text";
        }
      } 
      
      function changeMode() {  // this changes the controls to accept file or text input
        if (mode == "encrypt") {
            document.getElementById("encrypt").style.display = "none";
            document.getElementById("dlist").start = 4;
            mode = "decrypt";
        } else {
            document.getElementById("encrypt").style.display = "block";
            document.getElementById("dlist").start = 1;
            mode = "encrypt";
        }     
        newEmsg();
        newDmsg();
      }
      
      function fileSelected(which) {   // this is handles the file selection event. which file is passed in
        var limit = 1024*1024;
        if (which == "emsgfile") {
          limit = 512*1024;
        }
        if(document.getElementById(which).files[0].size > limit ) {
          alert("Please select a file smaller than " + limit + " bytes.");
        } else {
          document.getElementById("loader").className = "loading";
          var reader = new FileReader(); 
          if (which == "epad") {   // select encoding pad file  
            readtype = "bin";
            reader.onloadend = function(e) {
              document.getElementById("erawfile").value = this.result;
              document.getElementById("emd5").value = hex_md5(this.result);
              document.getElementById("loader").className = "ready";
            };  
          } else {
            if (which == "dpad") {  // select decoding pad file   
              readtype = "bin";
              reader.onloadend = function(e) {
                document.getElementById("drawfile").value = this.result;
                document.getElementById("dmd5").value = hex_md5(this.result);
                document.getElementById("loader").className = "ready";
              }; 
            } else {
              if (which == "emsgfile") {  // select file to encode    
                readtype = "img";
                reader.onloadend = function(e) {
                  filename = document.getElementById(which).files[0].name;
                  filetype = document.getElementById(which).files[0].type;
                  fromfile = true;
                  if (this.result.substr(0,1) != " ") {
                    document.getElementById("erawmsgin").value =  "From " + filetype.replace("/ /g","") + " file named " + filename.replace("/ /g","_") + " " + this.result;
                  } else {
                    document.getElementById("erawmsgin").value = this.result;
                  }
                  document.getElementById("loader").className = "ready";
                }; 
              } else {  // select message file to decode
                readtype = "txt";
                reader.onloadend = function(e) {
                  document.getElementById("dcyphertextIn").value = this.result;
                  document.getElementById("loader").className = "ready";
                }; 
              }
            }                         
          }
          if (readtype == "img") {
            reader.readAsDataURL(document.getElementById(which).files[0]);
          } else {
            if (readtype == "txt") {
              reader.readAsText(document.getElementById(which).files[0]);
            } else {
              reader.readAsBinaryString(document.getElementById(which).files[0]);
            }
          }      
        }
      }
      
      function makeRepeatableKey(start,count) {   // this makes a replicatable string from which to make a key / iv pair
        var result = start;
        for (var i = 0; i < count ; i++) {
          result = CryptoJS3.SHA3("baltic sea salt " + result + " course ground black pepper", { outputLength: 384 });
        }
        return result;
      }
      
      function makeRandomKey(count) {    // this makes a shorter random string from which to make a key / iv pair
        notLocked = false;  // this prevents a race condition for entropy addition. the timed process bypasses it while this is running 
        addEntropy(); 
        var result = CryptoJS2.SHA256("sea salt " + seedKey + " course ground black pepper corns" );
        for (var i = 0; i < count ; i++) {    
          addEntropy();
          result = CryptoJS2.SHA256("rock salt " + result + " fine ground black pepper " + seedKey);
        }             
        notLocked = true;    
        return result;
      }
      
      function buildPad(prefix) {  // this builds the one-time pad by encrypting the File Contents and converting the result to hex
        document.getElementById("loader").className = "loading";
        var master = makeRepeatableKey(document.getElementById(prefix + "phrase").value,616);
        var key = CryptoJS.enc.Hex.parse(CryptoJS.enc.Hex.stringify(master).substr(0,64));
        var iv  = CryptoJS.enc.Hex.parse(CryptoJS.enc.Hex.stringify(master).substr(64,32));
        document.getElementById(prefix + "rawpad").value = CryptoJS.AES.encrypt(document.getElementById(prefix + "rawfile").value, key, { iv: iv } );
        document.getElementById(prefix + "hexpad").value = bin2hex(decode64(document.getElementById(prefix + "rawpad").value));
        document.getElementById(prefix + "ready").innerHTML = "One-Time Book is Ready";
        document.getElementById("loader").className = "ready";
      }
      
      function reBuildPad(prefix) { // this rebuilds the one-time pad by reencrypting the File Contents and converting the result to hex
        var master = makeRepeatableKey(document.getElementById(prefix + "hexpad").value,42);
        var key = CryptoJS.enc.Hex.parse(CryptoJS.enc.Hex.stringify(master).substr(0,64));
        var iv  = CryptoJS.enc.Hex.parse(CryptoJS.enc.Hex.stringify(master).substr(64,32));
        document.getElementById(prefix + "rawpad").value = CryptoJS.AES.encrypt(document.getElementById(prefix + "rawfile").value, key, { iv: iv } );
        document.getElementById(prefix + "hexpad").value = bin2hex(decode64(document.getElementById(prefix + "rawpad").value));
      }
      
      function encryptMsg() {     // this aes encrypts the message to randomize the characters contained and converts the result to hex
        document.getElementById("loader").className = "loading";
        var thisKey = CryptoJS.enc.Hex.stringify( makeRandomKey(666));
        document.getElementById("ekey").value = thisKey;
        document.getElementById("eb64msgin").value = '' + CryptoJS.AES.encrypt( document.getElementById("erawmsgin").value , thisKey );
        document.getElementById("ehexmsgin").value = thisKey + bin2hex(document.getElementById("eb64msgin").value) ;
        document.getElementById("loader").className = "ready";
      }
      
      function reallyEncryptMsg() {    // this uses the hex book to do a book cypher encryption of the aes encrypted hex message
        document.getElementById("loader").className = "loading";  
        var lastnumb = 0;
        j = 0;
        //var delim = ""; 
        var encodedMsg = new Array();
        document.getElementById("ecyphertext").value = "";
        for (i = 0; i < document.getElementById("ehexmsgin").value.length; i++ ) {
          k = document.getElementById("ehexpad").value.indexOf(document.getElementById("ehexmsgin").value.substr(i,1),j);
          if ( k < 0 ) {
              if (j == 0) {
                alert("Please select a longer file.");
                break;
              } else {
                j = 0;     
                reBuildPad("e");
                if (view == "tutorial") {
                  alert("Extending One-Time Book in place to encrypt character " + i + " of " + document.getElementById("ehexmsgin").value.length);
                }
                k = document.getElementById("ehexpad").value.indexOf(document.getElementById("ehexmsgin").value.substr(i,1),j);
                if ( k < lastnumb ) {
                  encodedMsg.push(k);
                  //document.getElementById("ecyphertext").value = document.getElementById("ecyphertext").value + delim + k; 
                } else { 
                  alert("Please select a longer file.");
                  break;
                }
              }
          } else {
            encodedMsg.push(k);
            //document.getElementById("ecyphertext").value = document.getElementById("ecyphertext").value + delim + k;  
          }
          //delim = ",";
          lastnumb = k;
          j = 1 + k;
        }
        document.getElementById("ecyphertext").value = encodedMsg.join(",");
        document.getElementById("ecyphertextHex").value = bin2hex(document.getElementById("ecyphertext").value);
        document.getElementById("ecyphertext64").value = encode64(document.getElementById("ecyphertext").value);
        var nownow = new Date;
        if (document.getElementById("ecyphertext").value.length < 512*1024) {
          var eDownload = new Blob([document.getElementById("ecyphertext").value], {type: 'text/plain'});
          document.getElementById("edownload").href = window.URL.createObjectURL(eDownload);
          document.getElementById("edownload").download = "File" + nownow.valueOf();
          document.getElementById("edownload").onclick = function() { return true; };
          document.getElementById("edownload").className="optional;"
          document.getElementById("edownload").innerHTML="Download";
        } else {
          document.getElementById("edownload").onclick = function() { return false; };
          document.getElementById("edownload").className="optional disabled";
          document.getElementById("edownload").innerHTML="Too Large to Download";
        }
        if (document.getElementById("ecyphertext64").value.length < 512*1024) {
          var eDownloadB64 = new Blob([document.getElementById("ecyphertext64").value], {type: 'text/plain'});
          document.getElementById("edownloadB64").href = window.URL.createObjectURL(eDownloadB64);
          document.getElementById("edownloadB64").download = "File" + nownow.valueOf() + "B64";
          document.getElementById("edownloadB64").onclick = function() { return true; };
          document.getElementById("edownloadB64").className="optional";
          document.getElementById("edownloadB64").innerHTML="Download";
         } else {
          document.getElementById("edownloadB64").onclick = function() { return false; };
          document.getElementById("edownloadB64").className="optional disabled";
          document.getElementById("edownloadB64").innerHTML="Too Large to Download";
        }
        if (document.getElementById("ecyphertextHex").value.length < 512*1024) {
          var eDownloadHex = new Blob([document.getElementById("ecyphertextHex").value], {type: 'text/plain'});
          document.getElementById("edownloadHex").href = window.URL.createObjectURL(eDownloadHex);
          document.getElementById("edownloadHex").download = "File" + nownow.valueOf() + "Hex";
          document.getElementById("edownloadHex").onclick = function() { return true; };
          document.getElementById("edownloadHex").className="optional";
          document.getElementById("edownloadHex").innerHTML="Download";
        } else {
          document.getElementById("edownloadHex").onclick = function() { return false; };  
          document.getElementById("edownloadHex").className="optional disabled";
          document.getElementById("edownloadHex").innerHTML="Too Large to Download";
        }
        document.getElementById("loader").className = "ready";
      }
     
     function decryptMsg() {   // this uses the replicated hex book to decrypt the Cyphertext Message to its aes encrypted form
       document.getElementById("loader").className = "loading";  
       var lastnumb = 0;
       var thisnumb = 0;
       if (document.getElementById("dataformat").selectedIndex == 0 ) {
         document.getElementById("dcyphertext").value = document.getElementById("dcyphertextIn").value;
       } else {
        if (document.getElementById("dataformat").selectedIndex == 1) {
          document.getElementById("dcyphertext").value = decode64(document.getElementById("dcyphertextIn").value);
        } else {
          document.getElementById("dcyphertext").value = hex2bin(document.getElementById("dcyphertextIn").value);
        }
       }
       var positions = document.getElementById("dcyphertext").value.split(",");
       var reassembledMsg = new Array();
       document.getElementById("dplaintextHex").value = "";
       for (i = 0; i < positions.length; i++ ) {
         thisnumb = 1 * positions[i];
         if ( lastnumb > thisnumb ) {
           if (view == "tutorial") {
              alert("Extending book in place to decrypt character " + i + " of " + positions.length);
           }
           reBuildPad("d");
         } 
         reassembledMsg.push(document.getElementById("dhexpad").value.substr(positions[i],1));
         //document.getElementById("dplaintextHex").value = document.getElementById("dplaintextHex").value + document.getElementById("dhexpad").value.substr(positions[i],1);
         lastnumb = thisnumb;
       }
       document.getElementById("dplaintextHex").value = reassembledMsg.join("");
       document.getElementById("loader").className = "ready";
     }
     
      function aesDecryptMsg() {   // this restores the aes Obfuscated Message to useful form
        document.getElementById("loader").className = "loading";  
        var recoveredKey = document.getElementById("dplaintextHex").value.substr(0,64);
        document.getElementById("dkey").value = recoveredKey;
        var msgLength = document.getElementById("dplaintextHex").value.length - 64 ;
        document.getElementById("dplaintextB64").value =  hex2bin(document.getElementById("dplaintextHex").value.substr(64,msgLength));
        var dcode =  CryptoJS.AES.decrypt( document.getElementById("dplaintextB64").value , recoveredKey );
        document.getElementById("dplaintext").value = dcode.toString(CryptoJS.enc.Utf8);
        var parts =  document.getElementById("dplaintext").value.split(" ");
        document.getElementById("ddownloadFile").style.display = "none";
        document.getElementById("dPic").style.display = "none"; 
        if ( parts.length > 5 ) {
          if ((parts[0] == "From") && (parts[2] == "file") && (parts[3] == "named"))   {
             var preamble = parts[0] + " " + parts[1] + " " + parts[2] + " " + parts[3] + " " + parts[4] + " ";
             var payload = document.getElementById("dplaintext").value.replace(preamble,"");
             if ( parts[1].split("/")[0] == "image") {
                document.getElementById("dPic").alt = parts[4] ;
                document.getElementById("dPic").src = payload ;
                document.getElementById("dPic").style.display = "block";
             }  
             document.getElementById("ddownloadFile").href = payload;
             document.getElementById("ddownloadFile").download = parts[4];
             document.getElementById("ddownloadFile").style.display = "block";
          } 
        }
        document.getElementById("loader").className = "ready";
      }
      
      </script>  
    <style type="text/css">     
          
      body {
      	font-family: arial, san-serif;
      }
      ol {
      	font-size: 16px;
      	font-weight: bold;
      }
      ul li {
      	font-weight: normal;
      	margin: 14px 0;
      }
      ol p {
      	font-size: 14px;
      	margin-bottom: 10px;
      }
      label {
      	font-weight: bold;
      	display: block;
      }
      label.inline {
      	display: inline;
      }
      span.petite {
      	font-weight: normal;
      	font-size: 12px;
      }
      #mode, #datatype {
      	margin-bottom: 1em;
      }
      #ddownloadFile {
      	width: 80px;
      }
      .accent {
      	font-size: 16px;
      	margin-right: 12px;
      }
      table, textarea, input {
      	width: 95%;
      	margin-bottom: 10px;
      }
      #dPic, #ddownloadFile, #emsgfilelabel, #emsgfile, #dfilelabel, #dfile {
      	display: none;
      }
      div.ready {
        font-style:italic;
      	font-size: 12px;
        position: relative;
      }   
      #eready {         
      	margin-bottom:15px;
      	top: -10px;
      }
      #dready {            
      	top: -25px;            	
      }
      a.optional {
      	font-size: 10px;
      	padding: 0 1px;
      	position: relative;
      	top: -2px;
      }
      a.disabled {
      	cursor: not-allowed;
      }
      a {
      	border: thin solid black;
      	background-color: #e1e1e1;
      	color: black;
      	font-weight: normal;
      	padding: 3px;
      	text-decoration: none;
      	font-size: 12px;
      	border-radius: 3px;
      	background: -webkit-linear-gradient(white, #cfcfcf);
      /* For Safari */
      	background: -o-linear-gradient(white, #cfcfcf);
      /* For Opera 11.1 to 12.0 */
      	background: -moz-linear-gradient(white, #cfcfcf);
      /* For Firefox 3.6 to 15 */
      	background: -irritating-browser-specific-linear-gradient(white, #cfcfcf);
      /* in protest of this crap */
      	background: linear-gradient(white, #cfcfcf);
      /* Standard syntax */
      }
      div.loading {
      	position: fixed;
      	height: 100%;
      	width: 100%;
      	left: 0;
      	top: 0;
      	cursor: wait;
      	background: #000;
      	opacity: .5;
      	z-index: 999
      }
      div.ready {
      	position: relative;
      	height: 0
      }
    </style>  
  </head>  
  <body>    <h1>Two-Factor, One-Time, Virtual Book Cypher Demo</h1>    <h2>This demo shows how a virtual book can be created to encode and decode a message.</h2>    
    <ul>
      <li>Since the book is virtual, it can be created differently for each message to act as a one-time pad.  Only one of the factors need be varied.
      </li>    
      <li>Also since the book is virtual, it can be lengthened as needed while in use to make it long enough to encrypt any message.
      </li>    
      <li>The two factors used to create the book are a file which both parties must have and a pass phrase that both parties must know.
      </li>    
      <li>Neither party needs to be connected to the internet to use this page after it's single source file is downloaded.
      </li>    
      <li>It is prudent that both parties be using computers with the same operating system, variations have not been tested.
      </li>    
      <li>Pre-sharing of the encryption factors is not strictly necessary as long as both parties agree on where to find them.
      </li>    
      <li>Unlike a physical one-time pad, it is not easy to destroy the encryption materials.
      </li>
    </ul>    <h2>So let's do it:</h2>    
    <form action="#" accept-charset="utf-8" enctype="multipart/form-data" method="POST">    
      <ol>
        <li>
        <label class="inline" for="mode">Select an action:
        </label>    
        <select id="mode" name="mode" onchange="changeMode();">    
          <option>Encrypt
          </option>       
          <option>Decrypt
          </option>    
        </select>
        </li>    
        <li>
        <label class="inline" for="datatype">Select a message type:
        </label>      
        <select id="datatype" name="datatype" onchange="changeType();">    
          <option>Text
          </option>       
          <option>File
          </option>    
        </select>
        </li>    
        <li>
        <label class="inline" for="tutorial">Select a view:
        </label>      
        <select id="tutorial" name="tutorial" onchange="changeView();">    
          <option>Functional
          </option>       
          <option>Tutorial
          </option>    
        </select>
        </li>
      </ol>    
      <div id="encrypt">    
        <ol start="4">
          <li>Get the file from which the One-Time Book will be made
          <p>    
            <label for="epad">Select a file to start with. This file is not the message. It should (but does not have to be) much larger than the message.  The recipient will use his copy of this file to decrypt the message.
            </label>    
            <input type="file" id="epad" name="epad" onchange="fileSelected(this.id);newEfile();">    
            <label id="erawfilelabel" for="erawfile">File Contents
            </label>    
<textarea id="erawfile" name="erawfile" rows="5">The File Contents will appear here</textarea>    
            <label for="emd5">MD5 hash of File Contents
            </label>    
            <input type="text" id="emd5" name="emd5" value="The MD5 hash of the File Contents will appear here"><br />    If there is trouble decrypting, check that this MD5 hash matches the MD5 hash of the recipient's copy of the file.     
          </p>
          </li>
          <li>Encrypt the file to turn it into the One-Time Book
          <p>    
            <label for="ephrase">Enter a pass phrase to use to encrypt the file.  This pass phrase is not the message. The recipient will need to enter it to decrypt the message.
            </label>    
            <input type="text" id="ephrase" name="ephrase" onchange="newEphrase()" value="Replace this poor pass phrase."><br />    
            <a href="javascript:document.getElementById('loader').className = 'loading';setTimeout(function(){buildPad('e')},200);">Make the One-Time Book by using the pass phrase to encrypt the file</a><br /><br />    
            <label id="erawpadlabel" for="erawpad">Encrypted File Contents in Base 64
            </label>    
<textarea id="erawpad" name="erawpad" rows="5">The Encrypted File Contents will appear here in Base 64</textarea>    
            <label id="ehexpadlabel" for="ehexpad">Encrypted File Contents in Hex which are used as the One-Time Book
            </label>    
<textarea id="ehexpad" name="ehexpad" rows="5">The Encrypted File Contents will appear here in Hex</textarea>    
          </p>
          <div id="eready" class="ready">One-Time Book is not ready
          </div>    
          </li>
          <li>Enter the message and then encrypt it in two steps
          <p>    
            <label id="emsgfilelabel" for="emsgfile">Select a File to Send
            </label>    
            <input type="file" id="emsgfile" name="emsgfile" onchange="fileSelected(this.id);newEmsg()">    
            <label for="erawmsgin">Plaintext Message
            </label>    
<textarea id="erawmsgin" name="erawmsgin" rows="5">Type or paste your Plaintext Message here</textarea><br />    
            <span class="accent">Step 1:
            </span>
            <a href="javascript:document.getElementById('loader').className = 'loading';setTimeout(function(){encryptMsg()},200);">Generate a random key and use it to AES encrypt the plaintext message so that the character frequencies are obfuscated</a><br /><br />    
            <label id="ekeylabel" for="ekey">Randomly Generated AES-128 Key and IV concatenated together, both in Hex, referred to as "the Key" for simplicity
            </label>    
            <input type="text" id="ekey" name="ekey" value="The Key will appear here as one string of hex digits.">    
            <label id="eb64msginlabel" for="eb64msgin">Obfuscated Message in Base 64
            </label>    
<textarea id="eb64msgin" name="eb64msgin" rows="5">The Obfuscated Message will appear here in Base 64</textarea>    
            <label id="ehexmsginlabel" for="ehexmsgin">Obfuscated Message in Hex with prepended Key, also in Hex
            </label>    
<textarea id="ehexmsgin" name="ehexmsgin" rows="5">The Key will appear here in Hex followed by the Obfuscated Message in Hex</textarea><br />    
            <span class="accent">Step 2:
            </span>
            <a href="javascript:document.getElementById('loader').className = 'loading';setTimeout(function(){reallyEncryptMsg()},200);">Use the One-Time Book to book-cypher encrypt the Hex digits of the Obfuscated Message</a><br /><br />    
            <label for="ecyphertext">Cyphertext as an Array 
              <a class="optional disabled" id="edownload" href="" download="">Download</a> 
              <span class="petite">(Click Contents Once to Select All, then use Control C to copy.  For large messages, be very patient.)
              </span>
            </label>    
<textarea id="ecyphertext" name="ecyphertext" rows="5" onclick="this.select();" >The Cyphertext Message will appear here as created</textarea><br />    
            <label for="ecyphertext64">Cyphertext in Base 64 
              <a class="optional disabled" id="edownloadB64" href="" download="">Download</a> 
              <span class="petite">(Click Contents Once to Select All, then use Control C to copy.  For large messages, be very patient.)
              </span>
            </label>    
<textarea id="ecyphertext64" name="ecyphertext64" rows="5" onclick="this.select();" >The Cyphertext Message will appear here in Base 64</textarea><br />    
            <label for="ecyphertextHex">Cyphertext in Hex 
              <a class="optional disabled" id="edownloadHex" href="" download="">Download</a> 
              <span class="petite">(Click Contents Once to Select All, then use Control C to copy.  For large messages, be very patient.)
              </span>
            </label>    
<textarea id="ecyphertextHex" name="ecyphertextHex" rows="5" onclick="this.select();" >The Cyphertext Message will appear here in Hex</textarea><br />    <b>Send one of the three versions of the cyphertext to the recipient.</b><br />    
          </p>
          <ul id="results">    
            <li>The recipient will need 
            <a href="https://virtual-book-cypher.googlecode.com/archive/2abe43747c63c80fe9b5a9fb3ceaade4c8c05fcd.zip">a copy of this page</a> to decrypt the message.
            </li>    
            <li>The recipient will need to know which file was used to make the One-Time Book.
            </li>    
            <li>The recipient will need to know the pass phrase used to make the One-Time Book.
            </li>    
            <li>Do not send these things electronically, especially with the cyphertext.
            </li>    
            <li>Do not use the same pass phrase / file combination twice, keep it a One-Time Book.
            </li>    
          </ul>    
          </li>    
        </ol>    <h2>This is how the decryption process will look to the recipient:</h2>    
      </div>    
      <div id="decrypt">    
        <ol id="dlist" start="1">
          <li>Get the same file that the sender used to create his One-Time Book
          <p>    
            <label for="dpad">Select your copy of the file that the sender used to encrypt the message.
            </label>    
            <input type="file" id="dpad" name="dpad" onchange="fileSelected(this.id);newDfile();"><br />    
            <label id="drawfilelabel" for="drawfile">File Contents
            </label>    
<textarea id="drawfile" name="drawfile" rows="5">The File Contents will appear here</textarea><br />    
            <label for="dmd5">MD5 hash of File Contents
            </label>    
            <input type="text" id="dmd5" name="dmd5" value="The MD5 hash of the File Contents will appear here"><br />    If there is trouble decrypting, check that this MD5 hash matches the MD5 hash of the sender's copy of the file.     
          </p>
          </li>
          <li>Encrypt the file in the same way to create a replica of the sender's One-Time Book
          <p>    
            <label for="dphrase">Enter the pass phrase that the sender used to encrypt the message.
            </label>    
            <input type="text" id="dphrase" name="dphrase" onchange="newDphrase()" value="Replace this poor pass phrase."><br />    
            <a href="javascript:document.getElementById('loader').className = 'loading';setTimeout(function(){buildPad('d')},200);">Replicate the sender's One-Time Book by using the same pass phrase to encrypt the same file</a><br /><br />    
            <label id="drawpadlabel" for="drawpad">Encrypted File Contents in Base 64
            </label>    
<textarea id="drawpad" name="drawpad" rows="5">The Encrypted File Contents will appear here in Base 64</textarea><br />    
            <label id="dhexpadlabel" for="dhexpad">Encrypted File Contents in Hex which are a replica of the sender's One-Time Book
            </label>    
<textarea id="dhexpad" name="dhexpad" rows="5">The Encrypted File Contents will appear here in Hex</textarea>             
          </p>
          <div id="dready" class="ready">One-Time Book is not ready
          </div>    
          </li>
          <li>Enter the cyphertext and decrypt it in two steps
          <p>    
            <label id="dfilelabel" for="dfile">Load the cyphertext from a file 
              <a class="optional" id="pasteinstead" href="javascript:neverMind();">Paste Instead</a>
            </label>    
            <input type="file" id="dfile" name="dfile" onchange="fileSelected(this.id);newDmsg();">    
            <label for="dcyphertextIn">Cyphertext 
              <a class="optional" id="loadfromfile" href="javascript:loadFromFile();">Load From File</a>
            </label>    
<textarea id="dcyphertextIn" name="dcyphertextIn" rows="5">Paste the Cyphertext Message here</textarea><br />    
            <label for="dataformat">What format was the message sent in?
            </label>    
            <select id="dataformat" name="dataformat">    
              <option>Array (1,2,3, etc.)
              </option>    
              <option>Base 64
              </option>    
              <option>Hex
              </option>    
            </select><br /><br />    
            <span class="accent">Step 1:
            </span>
            <a href="javascript:document.getElementById('loader').className = 'loading';setTimeout(function(){decryptMsg()},200);">Use the replicated One-Time Book to book-cypher decrypt the cyphertext into the obfuscated version of the original message</a><br /><br />    
            <label id="dcyphertextlabel" for="dcyphertext">Cyphertext as an Array
            </label>    
<textarea id="dcyphertext" name="dcyphertext" rows="5">The Cyphertext will appear here as an array in case it did not arrive that way.</textarea><br />    
            <label id="dplaintextHexlabel" for="dplaintextHex">Obfuscated Message in Hex with prepended Key, also in Hex
            </label>    
<textarea id="dplaintextHex" name="dplaintextHex" rows="5">The Key will appear here in Hex followed by the Obfuscated Message in Hex</textarea><br />    
            <span class="accent">Step 2:
            </span>
            <a href="javascript:document.getElementById('loader').className = 'loading';setTimeout(function(){aesDecryptMsg()},200);">Recover the Key and use it to AES decrypt the Plaintext Message</a><br /><br />      
            <label id="dkeylabel" for="dkey">Recovered AES-128 Key and IV concatenated together, both in Hex, referred to as "the Key" for simplicity
            </label>    
            <input type="text" id="dkey" name="dkey" value="The recovered Key will appear here as one string of hex digits."><br />    
            <label id="dplaintextB64label" for="dplaintextB64">Obfuscated Message in Base 64
            </label>    
<textarea id="dplaintextB64" name="dplaintextB64" rows="5">The Obfuscated Message will appear here in Base 64</textarea><br />    
            <label for="dplaintext">Plaintext Message
            </label>    
<textarea id="dplaintext" name="dplaintext" rows="5">The Plaintext Message will appear here</textarea><br />    
            <img id="dPic" alt="" src=""><br />    
            <a id="ddownloadFile" href="" download="">Download File</a>    
          </p>
          </li>    
        </ol>    
      </div>    
    </form>    
    <div id="loader" class="ready">&nbsp;
    </div>    
<script type="text/javascript">
    
    // disable links
    
    document.getElementById("edownloadHex").onclick = function() { return false; };
    document.getElementById("edownloadB64").onclick = function() { return false; };
    document.getElementById("edownload").onclick = function() { return false; };
    
    // this sets up global error handling
    
    window.onerror = function(msg, url, line) {
   // You can view the information in an alert to see things working
   // like so:
   alert("A JavaScript error has occurred: " + msg + "\nurl: " + url + "\nline #: " + line);
   // TODO: Report this error via ajax so you can keep track
   //       of what pages have JS issues
   var suppressErrorAlert = true;
   document.getElementById("loader").className = "ready";
   // If you return true, then error alerts (like in older versions of 
   // Internet Explorer) will be suppressed.
   return suppressErrorAlert;
};
  
  // this part shows or hides detail
  
  function changeView() {  // this either shows or hides non-essential alerts and fields
    if (document.getElementById("tutorial").selectedIndex > 0 ) {
       document.getElementById("erawfilelabel").style.display = "block";
       document.getElementById("erawfile").style.display = "block";
       document.getElementById("erawpadlabel").style.display = "block";
       document.getElementById("erawpad").style.display = "block";
       document.getElementById("ehexpadlabel").style.display = "block";
       document.getElementById("ehexpad").style.display = "block";
       document.getElementById("eb64msginlabel").style.display = "block";
       document.getElementById("eb64msgin").style.display = "block";
       //.getElementById("ehexmsginlabel").style.display = "block";
       //document.getElementById("ehexmsgin").style.display = "block";
       document.getElementById("ekeylabel").style.display = "block";
       document.getElementById("ekey").style.display = "block";      
       document.getElementById("eready").style.display = "none";
       
       document.getElementById("drawfilelabel").style.display = "block";
       document.getElementById("drawfile").style.display = "block";
       document.getElementById("drawpadlabel").style.display = "block";
       document.getElementById("drawpad").style.display = "block";
       document.getElementById("dhexpadlabel").style.display = "block";
       document.getElementById("dhexpad").style.display = "block";
       document.getElementById("dcyphertextlabel").style.display = "block";
       document.getElementById("dcyphertext").style.display = "block";
       //document.getElementById("dplaintextHexlabel").style.display = "block";
       //document.getElementById("dplaintextHex").style.display = "block";
       document.getElementById("dkeylabel").style.display = "block";
       document.getElementById("dkey").style.display = "block";
       document.getElementById("dplaintextB64label").style.display = "block";
       document.getElementById("dplaintextB64").style.display = "block";      
       document.getElementById("dready").style.display = "none";
        //document.getElementById("dlist").start = 4;
        view = "tutorial";
    } else {
    document.getElementById("erawfilelabel").style.display = "none";
       document.getElementById("erawfile").style.display = "none";
       document.getElementById("erawpadlabel").style.display = "none";
       document.getElementById("erawpad").style.display = "none";
       document.getElementById("ehexpadlabel").style.display = "none"; 
       document.getElementById("ehexpad").style.display = "none";
       document.getElementById("eb64msginlabel").style.display = "none";
       document.getElementById("eb64msgin").style.display = "none";
       //document.getElementById("ehexmsginlabel").style.display = "none";
       //document.getElementById("ehexmsgin").style.display = "none";
       document.getElementById("ekeylabel").style.display = "none"; 
       document.getElementById("ekey").style.display = "none";              
       document.getElementById("eready").style.display = "block";
    
      document.getElementById("drawfilelabel").style.display = "none";
       document.getElementById("drawfile").style.display = "none";
       document.getElementById("drawpadlabel").style.display = "none";
       document.getElementById("drawpad").style.display = "none";
       document.getElementById("dhexpadlabel").style.display = "none"; 
       document.getElementById("dhexpad").style.display = "none";
       document.getElementById("dcyphertextlabel").style.display = "none";
       document.getElementById("dcyphertext").style.display = "none"; 
       //document.getElementById("dplaintextHexlabel").style.display = "none"; 
       //document.getElementById("dplaintextHex").style.display = "none"; 
       document.getElementById("dkeylabel").style.display = "none"; 
       document.getElementById("dkey").style.display = "none"; 
       document.getElementById("dplaintextB64label").style.display = "none";
       document.getElementById("dplaintextB64").style.display = "none";            
       document.getElementById("dready").style.display = "block";
       view = "functional";
    }
  }
  
  changeView();
  
  // this section handles resets to the gui when revisions are made  
    
  
    
  var inputControls = document.getElementsByTagName("INPUT");
  var textareaControls = document.getElementsByTagName("TEXTAREA");
  var dFaults = new Array();
  var a = 0;
  for ( a=0 ; a < inputControls.length ; a++ ) {
    dFaults[inputControls[a].id] = inputControls[a].value ;
  }
  for ( a=0 ; a < textareaControls.length ; a++ ) {
    dFaults[textareaControls[a].id] = textareaControls[a].value ;
  }
  
  function newEfile() {
    newEphrase();
  }
   
  function newDfile() { 
    newDphrase();
  }
  
  function newEphrase() {
    document.getElementById("erawpad").value = dFaults["erawpad"];
    document.getElementById("ehexpad").value = dFaults["ehexpad"];   
    document.getElementById("eready").innerHTML = "One-Time Book is not ready";
    newEmsg();
  }
   
  function newDphrase() { 
    document.getElementById("drawpad").value = dFaults["drawpad"];
    document.getElementById("dhexpad").value = dFaults["dhexpad"]; 
    document.getElementById("dready").innerHTML = "One-Time Book is not ready";
    newDmsg();
  }
   
  function newEmsg() {
    document.getElementById("ekey").value = dFaults["ekey"];
    document.getElementById("eb64msgin").value = dFaults["eb64msgin"];
    document.getElementById("ehexmsgin").value = dFaults["ehexmsgin"];
    document.getElementById("ecyphertext").value = dFaults["ecyphertext"];
    document.getElementById("ecyphertext64").value = dFaults["ecyphertext64"];
    document.getElementById("ecyphertextHex").value = dFaults["ecyphertextHex"];
    document.getElementById("edownload").href = "";
    document.getElementById("edownload").download = "";
    document.getElementById("edownloadB64").href = "";
    document.getElementById("edownloadB64").download = "";
    document.getElementById("edownloadHex").href = "";
    document.getElementById("edownloadHex").download = "";
  }
  
  function newDmsg() {
    document.getElementById("dcyphertext").value = dFaults["dcyphertext"];
    document.getElementById("dplaintextHex").value = dFaults["dplaintextHex"];
    document.getElementById("dkey").value = dFaults["dkey"];
    document.getElementById("dplaintextB64").value = dFaults["dplaintextB64"];
    document.getElementById("dplaintext").value = dFaults["dplaintext"];
    document.getElementById("ddownloadFile").style.display = "none";
    document.getElementById("dPic").style.display = "none"; 
  }
          
      //Utility operations
  function checkBin(n){return/^[01]{1,64}$/.test(n)}
  function checkDec(n){return/^[0-9]{1,64}$/.test(n)}
  function checkHex(n){return/^[0-9A-Fa-f]{1,64}$/.test(n)}
  function pad(s,z){s=""+s;return s.length<z?pad("0"+s,z):s}
  function unpad(s){s=""+s;return s.replace(/^0+/,'')}
  
  //Decimal operations
  function Dec2Bin(n){if(!checkDec(n)||n<0)return 0;return n.toString(2)}
  function Dec2Hex(n){if(!checkDec(n)||n<0)return 0;return n.toString(16)}
  
  //Binary Operations
  function Bin2Dec(n){if(!checkBin(n))return 0;return parseInt(n,2).toString(10)}
  function Bin2Hex(n){if(!checkBin(n))return 0;return parseInt(n,2).toString(16)}
  
  //Hexadecimal Operations
  function Hex2Bin(n){if(!checkHex(n))return 0;return parseInt(n,16).toString(2)}
  function Hex2Dec(n){if(!checkHex(n))return 0;return parseInt(n,16).toString(10)}
  function bin2hex(bin) { 
    var i = 0, l = bin.length, chr, hex = '';
    for (i; i < l; ++i) {     
      chr = bin.charCodeAt(i).toString(16);    
      hex += chr.length < 2 ? '0' + chr : chr; 
    } 
    return hex ;  
  }
  function hex2bin(hex) {
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
  }
    
//  The next sections contain attributed scripts from others
    
      /**
 *  CryptoMX Tools
 *  Copyright (C) 2004 - 2006 Derek Buitenhuis
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Modified February 2009 by TimStamp.co.uk - Modifications include:
 * + moved keyStr inside functions to prevent external variable clashes
 * + added regular expression replace terms to remove illegal characters
 * + added code to encode64(str) function to add the correct number of equals
 *   signs, which are supposed to be used to pad the output string to a
 *   length divisible by 4. This prevents the decode64(str) function from 
 *   adding unwanted null values at the end of the output.
 * + Also added line-breaks on the encode function at 64 chars.
 **/
 
  function encode64(input) {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    //remove null characters from end of output
    input = input.replace(/\0*$/g, '');
    
    var output = "";
    var chr1, chr2, chr3 = "";
    var enc1, enc2, enc3, enc4 = "";
    var i = 0;
    do {
        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);
        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;
        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }
        output = output +
            keyStr.charAt(enc1) +
            keyStr.charAt(enc2) +
            keyStr.charAt(enc3) +
            keyStr.charAt(enc4);
        chr1 = chr2 = chr3 = "";
        enc1 = enc2 = enc3 = enc4 = "";
    } while (i < input.length);
    var strout = ""
    output = output.split('');
    for(var c=0; c<output.length; c++) {
        if(c % 64 == 0 && c > 0) strout += '\n';
        strout += output[c];
    }
    output = output.join();
    
    var nulls = strout % 4;
    for(var i=0; i<nulls; i++)
        strout += '=';
    
    return strout;
}
  function decode64(input) {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var output = "";
    var chr1, chr2, chr3 = "";
    var enc1, enc2, enc3, enc4 = "";
    var i = 0;
    // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
    input = input.replace(/[^A-Za-z0-9\+\/\=\/n]/g, "");
    do {
        enc1 = keyStr.indexOf(input.charAt(i++));
        enc2 = keyStr.indexOf(input.charAt(i++));
        enc3 = keyStr.indexOf(input.charAt(i++));
        enc4 = keyStr.indexOf(input.charAt(i++));
        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;
        output = output + String.fromCharCode(chr1);
        if (enc3 != 64) {
            output = output + String.fromCharCode(chr2);
        }
        if (enc4 != 64) {
            output = output + String.fromCharCode(chr3);
        }
        chr1 = chr2 = chr3 = "";
        enc1 = enc2 = enc3 = enc4 = "";
    } while (i < input.length);
    
    //remove null characters from end of output
    output = output.replace(/\0*$/g, "");
    return output;
  } 
  
/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */
/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}
/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);
}
/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }
  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}
/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}
/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}
/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}
/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}
/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}
/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
l)}})();
CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();
    
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
Modified 12/25/2013 by Fred Scott Thompson to use a different non-conflicting variable name
*/
var CryptoJS2=CryptoJS2||function(h,s){var f={},t=f.lib={},g=function(){},j=t.Base={extend:function(a){g.prototype=this;var c=new g;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
q=t.WordArray=j.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||u).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=j.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push(4294967296*h.random()|0);return new q.init(c,a)}}),v=f.enc={},u=v.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b,
2),16)<<24-4*(b%8);return new q.init(d,c/2)}},k=v.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new q.init(d,c)}},l=v.Utf8={stringify:function(a){try{return decodeURIComponent(escape(k.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return k.parse(unescape(encodeURIComponent(a)))}},
x=t.BufferedBlockAlgorithm=j.extend({reset:function(){this._data=new q.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=l.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var m=0;m<a;m+=e)this._doProcessBlock(d,m);m=d.splice(0,a);c.sigBytes-=b}return new q.init(m,b)},clone:function(){var a=j.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});t.Hasher=x.extend({cfg:j.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){x.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new w.HMAC.init(a,
d)).finalize(c)}}});var w=f.algo={};return f}(Math);
(function(h){for(var s=CryptoJS2,f=s.lib,t=f.WordArray,g=f.Hasher,f=s.algo,j=[],q=[],v=function(a){return 4294967296*(a-(a|0))|0},u=2,k=0;64>k;){var l;a:{l=u;for(var x=h.sqrt(l),w=2;w<=x;w++)if(!(l%w)){l=!1;break a}l=!0}l&&(8>k&&(j[k]=v(h.pow(u,0.5))),q[k]=v(h.pow(u,1/3)),k++);u++}var a=[],f=f.SHA256=g.extend({_doReset:function(){this._hash=new t.init(j.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],m=b[2],h=b[3],p=b[4],j=b[5],k=b[6],l=b[7],n=0;64>n;n++){if(16>n)a[n]=
c[d+n]|0;else{var r=a[n-15],g=a[n-2];a[n]=((r<<25|r>>>7)^(r<<14|r>>>18)^r>>>3)+a[n-7]+((g<<15|g>>>17)^(g<<13|g>>>19)^g>>>10)+a[n-16]}r=l+((p<<26|p>>>6)^(p<<21|p>>>11)^(p<<7|p>>>25))+(p&j^~p&k)+q[n]+a[n];g=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&m^f&m);l=k;k=j;j=p;p=h+r|0;h=m;m=f;f=e;e=r+g|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+m|0;b[3]=b[3]+h|0;b[4]=b[4]+p|0;b[5]=b[5]+j|0;b[6]=b[6]+k|0;b[7]=b[7]+l|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=g.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=g._createHelper(f);s.HmacSHA256=g._createHmacHelper(f)})(Math);
    
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
Modified 12/25/2013 by Fred Scott Thompson to use a different non-conflicting variable name
*/
var CryptoJS3=CryptoJS3||function(v,p){var d={},u=d.lib={},r=function(){},f=u.Base={extend:function(a){r.prototype=this;var b=new r;a&&b.mixIn(a);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
s=u.WordArray=f.extend({init:function(a,b){a=this.words=a||[];this.sigBytes=b!=p?b:4*a.length},toString:function(a){return(a||y).stringify(this)},concat:function(a){var b=this.words,c=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var n=0;n<a;n++)b[j+n>>>2]|=(c[n>>>2]>>>24-8*(n%4)&255)<<24-8*((j+n)%4);else if(65535<c.length)for(n=0;n<a;n+=4)b[j+n>>>2]=c[n>>>2];else b.push.apply(b,c);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<
32-8*(b%4);a.length=v.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],c=0;c<a;c+=4)b.push(4294967296*v.random()|0);return new s.init(b,a)}}),x=d.enc={},y=x.Hex={stringify:function(a){var b=a.words;a=a.sigBytes;for(var c=[],j=0;j<a;j++){var n=b[j>>>2]>>>24-8*(j%4)&255;c.push((n>>>4).toString(16));c.push((n&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],j=0;j<b;j+=2)c[j>>>3]|=parseInt(a.substr(j,
2),16)<<24-4*(j%8);return new s.init(c,b/2)}},e=x.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var c=[],j=0;j<a;j++)c.push(String.fromCharCode(b[j>>>2]>>>24-8*(j%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],j=0;j<b;j++)c[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new s.init(c,b)}},q=x.Utf8={stringify:function(a){try{return decodeURIComponent(escape(e.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return e.parse(unescape(encodeURIComponent(a)))}},
t=u.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new s.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=q.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,c=b.words,j=b.sigBytes,n=this.blockSize,e=j/(4*n),e=a?v.ceil(e):v.max((e|0)-this._minBufferSize,0);a=e*n;j=v.min(4*a,j);if(a){for(var f=0;f<a;f+=n)this._doProcessBlock(c,f);f=c.splice(0,a);b.sigBytes-=j}return new s.init(f,j)},clone:function(){var a=f.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});u.Hasher=t.extend({cfg:f.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){t.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,c){return(new a.init(c)).finalize(b)}},_createHmacHelper:function(a){return function(b,c){return(new w.HMAC.init(a,
c)).finalize(b)}}});var w=d.algo={};return d}(Math);
(function(v){var p=CryptoJS3,d=p.lib,u=d.Base,r=d.WordArray,p=p.x64={};p.Word=u.extend({init:function(f,s){this.high=f;this.low=s}});p.WordArray=u.extend({init:function(f,s){f=this.words=f||[];this.sigBytes=s!=v?s:8*f.length},toX32:function(){for(var f=this.words,s=f.length,d=[],p=0;p<s;p++){var e=f[p];d.push(e.high);d.push(e.low)}return r.create(d,this.sigBytes)},clone:function(){for(var f=u.clone.call(this),d=f.words=this.words.slice(0),p=d.length,r=0;r<p;r++)d[r]=d[r].clone();return f}})})();
(function(v){for(var p=CryptoJS3,d=p.lib,u=d.WordArray,r=d.Hasher,f=p.x64.Word,d=p.algo,s=[],x=[],y=[],e=1,q=0,t=0;24>t;t++){s[e+5*q]=(t+1)*(t+2)/2%64;var w=(2*e+3*q)%5,e=q%5,q=w}for(e=0;5>e;e++)for(q=0;5>q;q++)x[e+5*q]=q+5*((2*e+3*q)%5);e=1;for(q=0;24>q;q++){for(var a=w=t=0;7>a;a++){if(e&1){var b=(1<<a)-1;32>b?w^=1<<b:t^=1<<b-32}e=e&128?e<<1^113:e<<1}y[q]=f.create(t,w)}for(var c=[],e=0;25>e;e++)c[e]=f.create();d=d.SHA3=r.extend({cfg:r.cfg.extend({outputLength:512}),_doReset:function(){for(var a=this._state=
[],b=0;25>b;b++)a[b]=new f.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(a,b){for(var e=this._state,f=this.blockSize/2,h=0;h<f;h++){var l=a[b+2*h],m=a[b+2*h+1],l=(l<<8|l>>>24)&16711935|(l<<24|l>>>8)&4278255360,m=(m<<8|m>>>24)&16711935|(m<<24|m>>>8)&4278255360,g=e[h];g.high^=m;g.low^=l}for(f=0;24>f;f++){for(h=0;5>h;h++){for(var d=l=0,k=0;5>k;k++)g=e[h+5*k],l^=g.high,d^=g.low;g=c[h];g.high=l;g.low=d}for(h=0;5>h;h++){g=c[(h+4)%5];l=c[(h+1)%5];m=l.high;k=l.low;l=g.high^
(m<<1|k>>>31);d=g.low^(k<<1|m>>>31);for(k=0;5>k;k++)g=e[h+5*k],g.high^=l,g.low^=d}for(m=1;25>m;m++)g=e[m],h=g.high,g=g.low,k=s[m],32>k?(l=h<<k|g>>>32-k,d=g<<k|h>>>32-k):(l=g<<k-32|h>>>64-k,d=h<<k-32|g>>>64-k),g=c[x[m]],g.high=l,g.low=d;g=c[0];h=e[0];g.high=h.high;g.low=h.low;for(h=0;5>h;h++)for(k=0;5>k;k++)m=h+5*k,g=e[m],l=c[m],m=c[(h+1)%5+5*k],d=c[(h+2)%5+5*k],g.high=l.high^~m.high&d.high,g.low=l.low^~m.low&d.low;g=e[0];h=y[f];g.high^=h.high;g.low^=h.low}},_doFinalize:function(){var a=this._data,
b=a.words,c=8*a.sigBytes,e=32*this.blockSize;b[c>>>5]|=1<<24-c%32;b[(v.ceil((c+1)/e)*e>>>5)-1]|=128;a.sigBytes=4*b.length;this._process();for(var a=this._state,b=this.cfg.outputLength/8,c=b/8,e=[],h=0;h<c;h++){var d=a[h],f=d.high,d=d.low,f=(f<<8|f>>>24)&16711935|(f<<24|f>>>8)&4278255360,d=(d<<8|d>>>24)&16711935|(d<<24|d>>>8)&4278255360;e.push(d);e.push(f)}return new u.init(e,b)},clone:function(){for(var a=r.clone.call(this),b=a._state=this._state.slice(0),c=0;25>c;c++)b[c]=b[c].clone();return a}});
p.SHA3=r._createHelper(d);p.HmacSHA3=r._createHmacHelper(d)})(Math);    
"use strict";
/*	============================================================================
									Gibson Research Corporation
				UHEPRNG - Ultra High Entropy Pseudo-Random Number Generator
	============================================================================
	LICENSE AND COPYRIGHT:  THIS CODE IS HEREBY RELEASED INTO THE PUBLIC DOMAIN
	Gibson Research Corporation releases and disclaims ALL RIGHTS AND TITLE IN
	THIS CODE OR ANY DERIVATIVES. Anyone may be freely use it for any purpose.
	============================================================================
	This is GRC's cryptographically strong PRNG (pseudo-random number generator)
	for JavaScript. It is driven by 1536 bits of entropy, stored in an array of
	48, 32-bit JavaScript variables.  Since many applications of this generator,
	including ours with the "Off The Grid" Latin Square generator, may require
	the deteriministic re-generation of a sequence of PRNs, this PRNG's initial
	entropic state can be read and written as a static whole, and incrementally
	evolved by pouring new source entropy into the generator's internal state.
	----------------------------------------------------------------------------
	Endless THANKS are due Johannes Baagoe for his careful development of highly
	robust JavaScript implementations of JS PRNGs.  This work was based upon his
	JavaScript "Alea" PRNG which is based upon the extremely robust Multiply-
	With-Carry (MWC) PRNG invented by George Marsaglia. MWC Algorithm References:
	http://www.GRC.com/otg/Marsaglia_PRNGs.pdf
	http://www.GRC.com/otg/Marsaglia_MWC_Generators.pdf
	----------------------------------------------------------------------------
	The quality of this algorithm's pseudo-random numbers have been verified by
	multiple independent researchers. It handily passes the fermilab.ch tests as
	well as the "diehard" and "dieharder" test suites.  For individuals wishing
	to further verify the quality of this algorithm's pseudo-random numbers, a
	256-megabyte file of this algorithm's output may be downloaded from GRC.com,
	and a Microsoft Windows scripting host (WSH) version of this algorithm may be
	downloaded and run from the Windows command prompt to generate unique files
	of any size:
	The Fermilab "ENT" tests: http://fourmilab.ch/random/
	The 256-megabyte sample PRN file at GRC: https://www.GRC.com/otg/uheprng.bin
	The Windows scripting host version: https://www.GRC.com/otg/wsh-uheprng.js
	----------------------------------------------------------------------------
	Qualifying MWC multipliers are: 187884, 686118, 898134, 1104375, 1250205,
	1460910 and 1768863. (We use the largest one that's < 2^21)
	============================================================================ */
    
//This version modified by Fred Scott Thompson on 12/25/2013 to only provide entrophy
function uheprng() {
	return (function() {
  		var o = 48;					// set the 'order' number of ENTROPY-holding 32-bit values
		var c = 1;					// init the 'carry' used by the multiply-with-carry (MWC) algorithm
		var p = o;					// init the 'phase' (max-1) of the intermediate variable pointer
		var s = new Array(o);	// declare our intermediate variables array
		var i,j,k=0;				// general purpose locals
		// when our "uheprng" is initially invoked our PRNG state is initialized from the
		// browser's own local PRNG. This is okay since although its generator might not
		// be wonderful, it's useful for establishing large startup entropy for our usage.		
		var mash = Mash();		// get a pointer to our high-performance "Mash" hash
		for (i = 0; i < o; i++) s[i] = mash( Math.random() );	// fill the array with initial mash hash values
		// this PRIVATE (internal access only) function is the heart of the multiply-with-carry
		// (MWC) PRNG algorithm. When called it returns a pseudo-random number in the form of a
		// 32-bit JavaScript fraction (0.0 to <1.0) it is a PRIVATE function used by the default
		// [0-1] return function, and by the random 'string(n)' function which returns 'n'
		// characters from 33 to 126.
		function rawprng() {
			if (++p >= o) p = 0;
			var t = 1768863 * s[p] + c * 2.3283064365386963e-10; // 2^-32
      	return s[p] = t - (c = t | 0);
		};
		
		// this EXPORTED function is the default function returned by this library.
		// The values returned are integers in the range from 0 to range-1. We first
		// obtain two 32-bit fractions (from rawprng) to synthesize a single high
		// resolution 53-bit prng (0 to <1), then we multiply this by the caller's
		// "range" param and take the "floor" to return a equally probable integer.
		var random = function( range ) {
			return Math.floor(range * (rawprng() + (rawprng() * 0x200000 | 0) * 1.1102230246251565e-16)); // 2^-53
		};
		// this EXPORTED function 'string(n)' returns a pseudo-random string of
		// 'n' printable characters ranging from chr(33) to chr(126) inclusive.
		random.string = function( count ) {
			var i, s='';
			for ( i=0; i<count; i++ ) s += String.fromCharCode( 33+random(94) );
			return s;
		};
		// this PRIVATE "hash" function is used to evolve the generator's internal
		// entropy state. It is also called by the EXPORTED addEntropy() function
		// which is used to pour entropy into the PRNG.
		function hash() {
			var args = Array.prototype.slice.call(arguments)
			for (i = 0; i < args.length; i++) {
				for (j = 0; j < o; j++) {
					s[j] -= mash(args[i]);
					if (s[j] < 0) s[j] += 1;
				}
			}
		};
		// this EXPORTED "clean string" function removes leading and trailing spaces and non-printing
		// control characters, including any embedded carriage-return (CR) and line-feed (LF) characters,
		// from any string it is handed. this is also used by the 'hashstring' function (below) to help
		// users always obtain the same EFFECTIVE uheprng seeding key.
		random.cleanString = function( inStr ) {
			inStr = inStr.replace(/(^\s*)|(\s*$)/gi,""); // remove any/all leading spaces
			inStr = inStr.replace(/[\x00-\x1F]/gi,"");	// remove any/all control characters
			inStr = inStr.replace(/\n /,"\n");				// remove any/all trailing spaces
			return inStr;											// return the cleaned up result
		}
		
		// this EXPORTED "hash string" function hashes the provided character string after first removing
		// any leading or trailing spaces and ignoring any embedded carriage returns (CR) or Line Feeds (LF)
		random.hashString = function( inStr ) {
			inStr = random.cleanString( inStr );
			mash( inStr );											// use the string to evolve the 'mash' state
			for ( i = 0; i < inStr.length; i++) {			// scan through the characters in our string
				k = inStr.charCodeAt( i );						// get the character code at the location
				for (j = 0; j < o; j++) {						//	"mash" it into the UHEPRNG state
					s[j] -= mash( k );
					if (s[j] < 0) s[j] += 1;
				}
			}
		};
				
		// this handy exported function is used to add entropy to our uheprng at any time
		random.addEntropy = function( /* accept zero or more arguments */ ) {
			var args = [];
			for ( i = 0; i < arguments.length; i++ ) args.push( arguments[i] );
			hash( (k++) + (new Date().getTime()) + args.join('') + Math.random() );
		};
		
		// if we want to provide a deterministic startup context for our PRNG,
		// but without directly setting the internal state variables, this allows
		// us to initialize the mash hash and PRNG's internal state before providing
		// some hashing input
		random.initState = function() {
			mash();													// pass a null arg to force mash hash to init
			for (i = 0; i < o; i++) s[i] = mash( ' ' );	// fill the array with initial mash hash values
			c = 1;													// init our multiply-with-carry carry
			p = o;													// init our phase
		};
		
		// we use this (optional) exported function to signal the JavaScript interpreter
		// that we're finished using the "Mash" hash function so that it can free up the
		// local "instance variables" is will have been maintaining.  It's not strictly
		// necessary, of course, but it's good JavaScript citizenship.
		random.done = function() {
			mash = null;
		};
		// when our main outer "uheprng" function is called, after setting up our
		// initial variables and entropic state, we return an "instance pointer"
		// to the internal anonymous function which can then be used to access
		// the uheprng's various exported functions.  As with the ".done" function
		// above, we should set the returned value to 'null' once we're finished
		// using any of these functions.
		return random;
  } ());
};
/*	============================================================================
	This is based upon Johannes Baagoe's carefully designed and efficient hash
	function for use with JavaScript.  It has a proven "avalanche" effect such
	that every bit of the input affects every bit of the output 50% of the time,
	which is good.	See: http://baagoe.com/en/RandomMusings/hash/avalanche.xhtml
	============================================================================
*/
function Mash() {
	var n = 0xefc8249d;
	var mash = function(data) {
		if ( data ) {
			data = data.toString();
			for (var i = 0; i < data.length; i++) {
				n += data.charCodeAt(i);
				var h = 0.02519603282416938 * n;
				n = h >>> 0;
				h -= n;
				h *= n;
				n = h >>> 0;
				h -= n;
				n += h * 0x1024*102400; // 2^32
			}
			return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
		} else n = 0xefc8249d;
	};
  return mash;
}    
  var seedKey = "";
	var prng = uheprng();	// instantiate our uheprng for requesting PRNs
	var eventCount = 0;		// this counts events to introduce a (small) bit of additional entropy
	var i,s = '';				// general purpose local vars
  var notLocked = true;
  
	periodicRehash();			// startup our periodic rehasher
	function addEntropyWrapper() {
     if (notLocked) {
        addEntropy();
     }
  }
  // this 'addEntropy' function calls the UHEPRNG's built-in hashing function with whatever (optional)
	// arguments it is provided, plus a count, the current time and a random value from the local browser
	// as a means of pouring additional entropy into the UHEPRNG's internal state.
	// Note that the invocation of the UHEPRNG initializes the PRNG with a large amount of initial entropy.
	function addEntropy() {
		prng.addEntropy();
		var prngState = prng.string(256);				// obtain 256 random printable characters
		for ( var s='',i=0; i < 8; i++ ) {					// for displaying, break the 256-chars into 8 lines of 32
			if ( i ) s += String.fromCharCode(10);			// firefox needs a CR/LF at the end of each 32-character line
			s += prngState.substr(i*32,32);					// concatenate the string pieces into a larger composite
		}
		seedKey = s;	// display the updated result in the webform
	}
	// this bit of "eye candy" is called upon page load and simply recalls itself five times per second. as
	// long as the "Randomize the Seed Key" option is enabled, it calls 'addEntropy()' to scramble the SeedKey
	function periodicRehash() {
		addEntropyWrapper();
		setTimeout( periodicRehash, 200 );	// re-call THIS function after the indicated interval
	}	
    </script>  
  </body>
</html>